import hashlib
import random
import time
import uuid
import xml.etree.ElementTree as ET
from datetime import datetime
import requests

from django.conf import settings
from rest_framework import serializers
from rest_framework.generics import ListAPIView
from rest_framework.response import Response
from rest_framework.views import APIView

from app01 import models


class LoginView(APIView):

    def post(self, request, *args, **kwargs):
        """ 用户登录 """
        phone = request.data.get('phone')
        wx_code = request.data.get('wx_code')
        # openid的获取:需要拿着wx_code去微信申请
        info = {
            'appid': "wx1c4068177526fbae",  # 好家猫微商城的
            'secret': "0c6c78c640e097cf1279a7296e2aaa39",  # 好家猫微商城的
            'js_code': wx_code,
            'grant_type': "authorization_code",
        }
        # 会返回session_key, openid, unionid
        result = requests.get(url='https://api.weixin.qq.com/sns/jscode2session', params=info)
        openid = result.json()['openid']

        exists = models.UserInfo.objects.filter(phone=phone).exists()
        token = str(uuid.uuid4())
        # 获取新令牌
        # APPID = settings.WECHAT_APPID
        # APPSECRET = settings.APP_SECRET
        # res = requests.get(
        #     f'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={APPID}&secret={APPSECRET}')
        # token = res.json()['access_token']
        if not exists:
            # 添加到数据库
            models.UserInfo.objects.create(
                phone=phone,
                token=token,
                openid=openid
            )
        else:
            # 更新数据库原有记录
            models.UserInfo.objects.filter(phone=phone).update(token=token, openid=openid)

        return Response({'token': token, 'openid': openid})


def md5(string):
    m = hashlib.md5()
    m.update(string.encode('utf-8'))
    return m.hexdigest()


class GoodsModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Goods
        fields = "__all__"


class GoodsView(ListAPIView):
    queryset = models.Goods.objects
    serializer_class = GoodsModelSerializer


class PaymentV2View(APIView):

    def post(self, request, *args, **kwargs):
        goods_id = request.data.get('goodsId')
        good_obj = models.Goods.objects.filter(id=goods_id).first()  # 里面有价格price
        uid_str = str(uuid.uuid4())[:32]  # 商户系统内部订单号
        # 用户对象.模拟的是1
        user_obj = models.UserInfo.objects.filter(id=1).first()  # 里面有openid
        # 在订单表中增加订单记录, user_id是登录客户的id,暂时定死了为1; uid为订单号,暂时定为str(uuid.uuid4())
        order_obj = models.Order.objects.create(goods=good_obj, user=user_obj, uid=uid_str, status=1)

        # 按照微信的规则,去生成支付时需要的一大堆数据
        # 1.调用支付统一下单
        info = {
            'appid': 'wx1c4068177526fbae',  # AppID(小程序ID)
            'mch_id': '1227281402',  # 商户号
            'device_info': 'zhou-min-program',  # 可选,可不填(使用什么设备收款的备注信息)
            'nonce_str': "".join([str(random.randint(65, 90)) for _ in range(12)]),
            # 'nonce_str': hashlib.md5(str(time.time()).encode()).hexdigest(),
            'sign_type': 'MD5',
            'body': '商城购物',
            'detail': '这是一个商品详细信息.',  # 可选,可不填
            'attach': '微信小程序',  # 可选,可不填
            'out_trade_no': uid_str,  # 商户系统内部订单号
            'total_fee': good_obj.price,  # 总金额
            'spbill_create_ip': request.META.get('REMOTE_ADDR'),  # 终端ip,即当前用户id
            # 'notify_url': 'http://139.155.127.236:8000/pay/notify/',  # 支付成功之后,微信异步通知(先用自己的腾讯云试验)
            'notify_url': 'https://3037747c.r7.cpolar.top/pay/notify/',  # 支付成功之后,微信异步通知(先用自己的腾讯云试验)
            'trade_type': 'JSAPI',
            'openid': user_obj.openid  # openid = user_obj.openid
        }

        # ################### 1. 签名 ###################
        # 对字典中的key按照ASCII码从小到大排序
        # 将排完序的值拼接stringA = appid=wx1c4068177526fbae&mch_id=1227281402
        # 让stringA和key拼按：stringSignTemp = stringA +"&key=20040125ZYFZWD20040125zyfzwd3232" key为商户平台设置的密钥key
        # MD5(stringSignTemp)
        # 将密文转换为大写
        # 得到签名sign
        # 把签名再添加到info中  info['sign']=sign值
        pay_key = '20040125ZYFZWD20040125zyfzwd3232'
        temp = "&".join(["{0}={1}".format(k, info[k]) for k in sorted(info)] + ["{0}={1}".format("key", pay_key, ), ])

        sign = md5(temp).upper()
        info['sign'] = sign

        # json需转换成xml
        xml_string = "<xml>{0}</xml>".format("".join(["<{0}>{1}</{0}>".format(k, v) for k, v in info.items()]))

        # 1.2 向https://api.mch.weixin.qq.com/pay/unifiedorder发送请求
        prepay = requests.post(url="https://api.mch.weixin.qq.com/pay/unifiedorder", data=xml_string.encode('utf-8'))
        # 1.3 从结果的xml中提取prepay_id
        result = ET.fromstring(prepay.content.decode('utf-8'))  # 强制用utf-8解码,并转为xml格式对象
        result_dict = {child.tag: child.text for child in result}  # xml对象转为字典
        prepay_id = result_dict['prepay_id']

        # ################### 2. 再次签名 ###################
        info_dict = {
            'appId': 'wx1c4068177526fbae',
            'timeStamp': str(int(time.time())),  # 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间
            'nonceStr': "".join([chr(random.randint(65, 90)) for _ in range(12)]),  # 随机字符串,长度为32个字符以下
            'package': 'prepay_id={0}'.format(prepay_id),
            # 统一下单接口返回的prepay_id,提交格式如: prepay_id = * (或f"prepay_id={result['prepay_id']}")
            'signType': 'MD5'  # 签名类型,默认为MD5,支持MAC_SHA256和MD5,注意此处需与统一下单的签名类型一致
        }
        temp = "&".join(
            ["{0}={1}".format(k, info_dict[k]) for k in sorted(info_dict)] + ["{0}={1}".format("key", pay_key), ])
        sign2 = md5(temp).upper()
        info_dict['paySign'] = sign2
        return Response(info_dict)


# class NotifyView(APIView):
#     """支付完成后的通知"""
#     def post(self, request, *args, **kwargs):
#         # 1. 腾讯会先给我们发送一个xml格式的数据 request.body
#         # 2. 将xml转换成json
#         root = ET.XML(request.body.decode('utf-8'))
#         result = {child.tag: child.text for child in root}
#
#         # 3. 去json中获取订单号 out_trade_no {appid: 'xx', ..., sign:asg125454fff, out_trade_no: 15019231}
#         # 4. 校验,防止不是同一个人同一个订单,被外界人爬虫或其他攻击造成免费付款成功了
#         # 4.1 先把sign从字典里移走,然后:字典 + key -> 密文
#         sign = result.pop('sign')
#         pay_key = '20040125ZYFZWD20040125zyfzwd3232'  # key为商户平台设置的密钥key
#         temp = "&".join(
#             ["{0}={1}".format(k, result[k]) for k in sorted(result)] + ["{0}={1}".format("key", pay_key, ), ])
#         local_sign = md5(temp).upper()
#
#         # 签名一致
#         if local_sign == sign:
#             print('V2版本--支付成功!!', datetime.now())
#
#             # 根据订单号, 把数据库的订单状态改为支付成功
#             out_trade_no = result.get('out_trade_no')
#             models.Order.objects.filter(uid=out_trade_no).update(status=2)
#             response = """
#                 <xml>
#                   <return_code><![CDATA[SUCCESS]]></return_code>
#                   <return_msg><![CDATA[OK]]></return_msg>
#                 </xml>
#             """
#         else:
#             print('V2版本--支付失败!!', datetime.now())
#             response = """
#                 <xml>
#                   <return_code><![CDATA[FAIL]]></return_code>
#                   <return_msg><![CDATA[签名失败]]></return_msg>
#                 </xml>
#             """
#
#         # 4.2 如果客户已经付完款到腾讯,腾讯正准备发通知给我们的服务器,恰好这时服务器宕机了,那么腾讯会在24小时内练续发,
#         #     直到服务器给腾讯返回特定的东西后,算交易结束
#         #     通知频率为:15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m
#         # 4.3 如果24小时内,服务器都没法恢复,那就没辙了,估计只能找腾讯官方客服了
#
#         return Response(response)


from django.db import transaction


class NotifyView(APIView):
    def post(self, request, *args, **kwargs):
        response_template = """
        <xml>
            <return_code><![CDATA[{return_code}]]></return_code>
            <return_msg><![CDATA[{return_msg}]]></return_msg>
        </xml>
        """
        try:
            # 解析XML并校验签名
            root = ET.XML(request.body.decode('utf-8'))
            result = {child.tag: child.text for child in root}
            sign = result.pop('sign')
            pay_key = '20040125ZYFZWD20040125zyfzwd3232'
            temp = "&".join(
                ["{0}={1}".format(k, result[k]) for k in sorted(result)] + ["key={0}".format(pay_key)]
            )
            # local_sign = md5(temp.encode('utf-8')).hexdigest().upper()
            local_sign = md5(temp).upper()

            if local_sign != sign:
                print('签名失败')
                return Response(response_template.format(return_code='FAIL', return_msg='签名失败'))

            # 幂等性检查：判断订单是否已处理
            out_trade_no = result.get('out_trade_no')
            order = models.Order.objects.filter(uid=out_trade_no).first()
            if not order:
                print('订单不存在')
                return Response(response_template.format(return_code='FAIL', return_msg='订单不存在'))
            if order.status == 2:
                print('订单存在,已返回腾讯SUCCESS和OK')
                return Response(response_template.format(return_code='SUCCESS', return_msg='OK'))

            # 数据库事务操作（避免部分更新）
            with transaction.atomic():
                models.Order.objects.filter(uid=out_trade_no).update(status=2)
                print('已更新数据库')

            print('最后return的SUCCESS和OK')
            return Response(response_template.format(return_code='SUCCESS', return_msg='OK'))

        except Exception as e:
            # 记录异常日志，但依然返回SUCCESS防止微信重试
            import logging
            logging.error(f"支付通知处理异常: {str(e)}")
            return Response(response_template.format(return_code='SUCCESS', return_msg='OK'))
